{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XGCdmDAKpLuf"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "GF4d1XplpLGF"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W1L3zJP6pPGD"
      },
      "source": [
        "# FGSM を使用した敵対的サンプル\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"><a>TensorFlow で表示</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tutorials/generative/adversarial_fgsm.ipynb\">Google Colab で実行</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\"><a target=\"_blank\" href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/tutorials/generative/adversarial_fgsm.ipynb\">GitHub でソースを表示</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/download_logo_32px.png\"><a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/generative/adversarial_fgsm.ipynb\">ノートブックをダウンロード</a>\n",
        "</td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8dn1-g8BpPDx"
      },
      "source": [
        "このチュートリアルでは、Goodfellow *など*が [Explaining and Harnessing Adversarial Examples](https://arxiv.org/abs/1412.6572) で説明する FGSM（Fast Gradient Signed Method）攻撃を使用して、*敵対的サンプル* を作成します。この攻撃は、ニューラルネットワークを騙した初の、広く使用される攻撃の 1 つです。\n",
        "\n",
        "## 敵対的サンプルとは？\n",
        "\n",
        "敵対的サンプルとは、ニューラルネットワークを混乱させて、ある入力が誤分類されることを目的に作成される特殊な入力です。このような悪名高い入力は人の目で区別できるものではありませんが、ネットワークが画像のコンテンツを識別できなくしてしまいます。このような攻撃にはいくつかの種類がありますが、ここでは、FGSM 攻撃という、誤分類を実現する目的を持つ*ホワイトボックス*攻撃に焦点を当てています。ホワイトボックス攻撃は、攻撃者が攻撃を受けるモデルに完全にアクセスできる状況を指します。以下に示される敵対的画像の最も有名な例の 1 つは、上記の資料から取得したものです。\n",
        "\n",
        "![Adversarial Example](images/adversarial_example.png)\n",
        "\n",
        "ここでは、攻撃者はパンダの画像から始め、元の画像に小さな摂動（歪み）を追加し、その結果、モデルは高信頼度でこの画像をテナガザルとして分類するようになります。この摂動の追加プロセスについて、以下で説明しています。\n",
        "\n",
        "## FGSM（Fast Gradient Sign Method）\n",
        "\n",
        "FGSM は、ニューラルネットワークの勾配を利用して敵対的サンプルを作成する手法です。入力画像に対し、入力画像に関する損失の勾配を使用して、その損失を最大化する新しい画像を作成します。この新しい画像は敵対的画像と呼ばれ、次の式を使って要約されます。$$adv_x = x + \\epsilon*\\text{sign}(\\nabla_xJ(\\theta, x, y))$$\n",
        "\n",
        "次のように解釈します。\n",
        "\n",
        "- adv_x : 敵対的画像\n",
        "- x : 元の入力画像\n",
        "- y : 元の入力ラベル\n",
        "- $\\epsilon$ : 小さな摂動を確実にするための乗数\n",
        "- $\\theta$ : モデルのパラメータ\n",
        "- $J$ : 損失\n",
        "\n",
        "ここで興味深い特性は、勾配が入力画像に関して取られているということです。これは、損失を最大化する画像を作成することを目的としているために行われています。画像内の各ピクセルがどれくらい損失値に貢献しているかを求め、それに応じて摂動を追加することで、これを達成しています。チェーンルールを使用して必要な勾配を見つけることで、各ピクセルの損失への貢献度を簡単に求めることができるため、非常に高速に処理することができます。したがって、勾配は画像に関して取られます。さらに、モデルはトレーニングされなくなるため（したがって、モデルパラメータであるトレーニング可能な変数に関して勾配が取られない）、モデルパラメータは一定したままになります。トレーニング済みのモデルを騙すことが唯一の目標です。\n",
        "\n",
        "では、トレーニング済みのモデルを騙してみましょう。このチュートリアルでは、[ImageNet](http://www.image-net.org/) で事前にトレーニングされた [MobileNetV2](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/applications/MobileNetV2) モデルを使用します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vag2WYR6yTOC"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "mpl.rcParams['figure.figsize'] = (8, 8)\n",
        "mpl.rcParams['axes.grid'] = False"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wiTHY8dqxzx7"
      },
      "source": [
        "トレーニング済みの MobileNetV2 モデルと ImageNet クラス名を読み込みましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nqhk2vYx6Ag0"
      },
      "outputs": [],
      "source": [
        "pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,\n",
        "                                                     weights='imagenet')\n",
        "pretrained_model.trainable = False\n",
        "\n",
        "# ImageNet labels\n",
        "decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f2cLrJH0zpfC"
      },
      "outputs": [],
      "source": [
        "# Helper function to preprocess the image so that it can be inputted in MobileNetV2\n",
        "def preprocess(image):\n",
        "  image = tf.cast(image, tf.float32)\n",
        "  image = tf.image.resize(image, (224, 224))\n",
        "  image = tf.keras.applications.mobilenet_v2.preprocess_input(image)\n",
        "  image = image[None, ...]\n",
        "  return image\n",
        "\n",
        "# Helper function to extract labels from probability vector\n",
        "def get_imagenet_label(probs):\n",
        "  return decode_predictions(probs, top=1)[0][0]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iEZaMVFgSUA-"
      },
      "source": [
        "## 元の画像\n",
        "\n",
        "ウィキペディアコモンズにある [ラブラドールレトリバー](https://commons.wikimedia.org/wiki/File:YellowLabradorLooking_new.jpg)（Mirko [CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)）のサンプル画像を使用して、敵対的サンプルを作成しましょう。この画像を事前処理し、MobileNetV2 モデルに入力としてフィードできるようにします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wpYrQ4OQSYWk"
      },
      "outputs": [],
      "source": [
        "image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')\n",
        "image_raw = tf.io.read_file(image_path)\n",
        "image = tf.image.decode_image(image_raw)\n",
        "\n",
        "image = preprocess(image)\n",
        "image_probs = pretrained_model.predict(image)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mvPlta_uSbuI"
      },
      "source": [
        "画像を見てみましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "99Jc-SNoSZot"
      },
      "outputs": [],
      "source": [
        "plt.figure()\n",
        "plt.imshow(image[0]*0.5+0.5) # To change [-1, 1] to [0,1]\n",
        "_, image_class, class_confidence = get_imagenet_label(image_probs)\n",
        "plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kElVTbF690CF"
      },
      "source": [
        "## 敵対的画像を作成する\n",
        "\n",
        "### FGSM を実装する\n",
        "\n",
        "まず、元の画像に歪ませて敵対的画像を生成するための摂動を作成します。すでに説明したように、このタスクでは、画像に関する勾配が取られます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FhZxlOnuBCVr"
      },
      "outputs": [],
      "source": [
        "loss_object = tf.keras.losses.CategoricalCrossentropy()\n",
        "\n",
        "def create_adversarial_pattern(input_image, input_label):\n",
        "  with tf.GradientTape() as tape:\n",
        "    tape.watch(input_image)\n",
        "    prediction = pretrained_model(input_image)\n",
        "    loss = loss_object(input_label, prediction)\n",
        "\n",
        "  # Get the gradients of the loss w.r.t to the input image.\n",
        "  gradient = tape.gradient(loss, input_image)\n",
        "  # Get the sign of the gradients to create the perturbation\n",
        "  signed_grad = tf.sign(gradient)\n",
        "  return signed_grad"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RbuftX0eSlDQ"
      },
      "source": [
        "これで得られた摂動を視覚化することもできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rVjnb6M7Smv4"
      },
      "outputs": [],
      "source": [
        "# Get the input label of the image.\n",
        "labrador_retriever_index = 208\n",
        "label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])\n",
        "label = tf.reshape(label, (1, image_probs.shape[-1]))\n",
        "\n",
        "perturbations = create_adversarial_pattern(image, label)\n",
        "plt.imshow(perturbations[0]*0.5+0.5); # To change [-1, 1] to [0,1]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DKKSFHjwCyQH"
      },
      "source": [
        "別の epsilon の値を使って、画像の変化を観察しましょう。epsilon の値が増加するにつれ、ネットワークを騙しやすくなるのがわかると思います。ただし、これには、摂動が識別しやすくなるというトレードオフがあります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dBtG0Kl5SspV"
      },
      "outputs": [],
      "source": [
        "def display_images(image, description):\n",
        "  _, label, confidence = get_imagenet_label(pretrained_model.predict(image))\n",
        "  plt.figure()\n",
        "  plt.imshow(image[0]*0.5+0.5)\n",
        "  plt.title('{} \\n {} : {:.2f}% Confidence'.format(description,\n",
        "                                                   label, confidence*100))\n",
        "  plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3DA8g-Zp69J4"
      },
      "outputs": [],
      "source": [
        "epsilons = [0, 0.01, 0.1, 0.15]\n",
        "descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')\n",
        "                for eps in epsilons]\n",
        "\n",
        "for i, eps in enumerate(epsilons):\n",
        "  adv_x = image + eps*perturbations\n",
        "  adv_x = tf.clip_by_value(adv_x, -1, 1)\n",
        "  display_images(adv_x, descriptions[i])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fxt5VfnXHQT6"
      },
      "source": [
        "## 次のステップ\n",
        "\n",
        "敵対的攻撃について理解できたので、ほかのデータベースやほかのアーキテクチャで試してみましょう。独自のモデルを作成してトレーニングしてから、同じ手法でそれを騙してみてもよいでしょう。また、epsilon を変更するにつれ、予測の信頼性がどのように変化するかを見てみるのもよいでしょう。\n",
        "\n",
        "このチュートリアルで紹介した攻撃は強力ですが、敵対的攻撃の研究の手始めにすぎません。これが明るみに出て以来、より強力な攻撃を作り出す多数の論文が発表されています。研究は、敵対的攻撃だけでなく、堅牢な機械学習モデルを作り出すことを目的に防衛をも生み出しています。敵対的攻撃と防衛の包括的なリストについては、こちらの[調査論文](https://arxiv.org/abs/1810.00069)をご覧ください。\n",
        "\n",
        "その他の敵対的攻撃と防衛の実装については、[CleverHans](https://github.com/tensorflow/cleverhans) という敵対的サンプルライブラリをご覧ください。"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "adversarial_fgsm.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
